"""
Data models for the Multi-Website Shared Login Management Tool.

This module defines the core data structures for managing website profiles,
user sessions, and global configuration settings.
"""

import json
import os
import time
from datetime import datetime, timedelta
from dataclasses import dataclass, asdict, field
from typing import Dict, List, Optional, Any, Union
from enum import Enum
import uuid


class AuthMethod(Enum):
    """Supported authentication methods."""
    FORM = "form"
    OAUTH = "oauth"
    MANUAL = "manual"
    CUSTOM = "custom"


class SessionStatus(Enum):
    """Session status types."""
    ACTIVE = "active"
    EXPIRED = "expired"
    INVALID = "invalid"
    PENDING = "pending"


@dataclass
class BrowserConfig:
    """Browser-specific configuration settings."""
    viewport: Dict[str, int] = field(default_factory=lambda: {"width": 1920, "height": 1080})
    user_agent: Optional[str] = None
    extra_args: List[str] = field(default_factory=list)
    extensions_enabled: bool = True
    headless: bool = False
    timeout: int = 30
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'BrowserConfig':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class SessionConfig:
    """Session-specific configuration settings."""
    timeout_hours: int = 24
    auto_refresh: bool = True
    backup_extensions: bool = True
    auto_sync: bool = True
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'SessionConfig':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class WebsiteSelectors:
    """CSS selectors for website automation."""
    username_field: Optional[str] = None
    password_field: Optional[str] = None
    submit_button: Optional[str] = None
    captcha_field: Optional[str] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'WebsiteSelectors':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class WebsiteProfile:
    """Website profile configuration."""
    website_id: str
    display_name: str
    target_url: str
    login_url: Optional[str] = None
    auth_method: AuthMethod = AuthMethod.MANUAL
    selectors: WebsiteSelectors = field(default_factory=WebsiteSelectors)
    custom_scripts: List[str] = field(default_factory=list)
    browser_config: BrowserConfig = field(default_factory=BrowserConfig)
    session_config: SessionConfig = field(default_factory=SessionConfig)
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None
    
    def __post_init__(self):
        """Post-initialization processing."""
        if self.created_at is None:
            self.created_at = datetime.now()
        if self.updated_at is None:
            self.updated_at = datetime.now()
        
        # Convert enum strings to enum objects if needed
        if isinstance(self.auth_method, str):
            self.auth_method = AuthMethod(self.auth_method)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        data = asdict(self)
        data['auth_method'] = self.auth_method.value
        data['created_at'] = self.created_at.isoformat() if self.created_at else None
        data['updated_at'] = self.updated_at.isoformat() if self.updated_at else None
        return data
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'WebsiteProfile':
        """Create from dictionary."""
        # Convert nested objects
        if 'selectors' in data and isinstance(data['selectors'], dict):
            data['selectors'] = WebsiteSelectors.from_dict(data['selectors'])
        
        if 'browser_config' in data and isinstance(data['browser_config'], dict):
            data['browser_config'] = BrowserConfig.from_dict(data['browser_config'])
        
        if 'session_config' in data and isinstance(data['session_config'], dict):
            data['session_config'] = SessionConfig.from_dict(data['session_config'])
        
        # Convert datetime strings
        if 'created_at' in data and isinstance(data['created_at'], str):
            data['created_at'] = datetime.fromisoformat(data['created_at'])
        
        if 'updated_at' in data and isinstance(data['updated_at'], str):
            data['updated_at'] = datetime.fromisoformat(data['updated_at'])
        
        return cls(**data)
    
    def update_timestamp(self):
        """Update the last modified timestamp."""
        self.updated_at = datetime.now()


@dataclass
class DeviceInfo:
    """Device identification metadata."""
    device_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    device_name: Optional[str] = None
    platform: Optional[str] = None
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'DeviceInfo':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class UserSession:
    """User session data for a specific website."""
    session_id: str
    website_id: str
    username: str
    auth_state: Dict[str, Any]
    extensions_data: Optional[bytes] = None
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None
    expires_at: Optional[datetime] = None
    device_info: Optional[DeviceInfo] = None
    status: SessionStatus = SessionStatus.PENDING
    
    def __post_init__(self):
        """Post-initialization processing."""
        if self.created_at is None:
            self.created_at = datetime.now()
        if self.updated_at is None:
            self.updated_at = datetime.now()
        if self.expires_at is None:
            # Default to 24 hours from creation
            self.expires_at = self.created_at + timedelta(hours=24)
        
        # Convert status string to enum if needed
        if isinstance(self.status, str):
            self.status = SessionStatus(self.status)
    
    def is_expired(self) -> bool:
        """Check if the session is expired."""
        return self.expires_at and datetime.now() > self.expires_at
    
    def is_valid(self) -> bool:
        """Check if the session is valid and active."""
        return (self.status == SessionStatus.ACTIVE and 
                not self.is_expired() and 
                bool(self.auth_state))
    
    def extend_expiry(self, hours: int = 24):
        """Extend the session expiry time."""
        self.expires_at = datetime.now() + timedelta(hours=hours)
        self.update_timestamp()
    
    def update_timestamp(self):
        """Update the last modified timestamp."""
        self.updated_at = datetime.now()
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        data = asdict(self)
        data['status'] = self.status.value
        data['created_at'] = self.created_at.isoformat() if self.created_at else None
        data['updated_at'] = self.updated_at.isoformat() if self.updated_at else None
        data['expires_at'] = self.expires_at.isoformat() if self.expires_at else None
        
        # Handle binary data
        if self.extensions_data:
            data['extensions_data'] = None  # Binary data handled separately
        
        return data
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'UserSession':
        """Create from dictionary."""
        # Convert nested objects
        if 'device_info' in data and isinstance(data['device_info'], dict):
            data['device_info'] = DeviceInfo.from_dict(data['device_info'])
        
        # Convert datetime strings
        for field_name in ['created_at', 'updated_at', 'expires_at']:
            if field_name in data and isinstance(data[field_name], str):
                data[field_name] = datetime.fromisoformat(data[field_name])
        
        # Remove extensions_data from dict initialization (handled separately)
        data.pop('extensions_data', None)
        
        return cls(**data)


@dataclass
class CloudStorageConfig:
    """Cloud storage configuration."""
    provider: str = "aliyun_oss"
    credentials: Dict[str, str] = field(default_factory=dict)
    bucket_name: str = "auth-share"
    region: str = "cn-shenzhen"
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'CloudStorageConfig':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class SyncSettings:
    """Synchronization settings."""
    auto_sync: bool = True
    sync_interval_minutes: int = 30
    conflict_resolution: str = "timestamp"  # timestamp, manual, latest
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'SyncSettings':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class BrowserSettings:
    """Global browser settings."""
    default_count: int = 1
    max_concurrent: int = 10
    default_timeout: int = 30
    extensions_backup: bool = True
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'BrowserSettings':
        """Create from dictionary."""
        return cls(**data)


@dataclass
class GlobalConfiguration:
    """Global system configuration."""
    cloud_storage: CloudStorageConfig = field(default_factory=CloudStorageConfig)
    browser_settings: BrowserSettings = field(default_factory=BrowserSettings)
    sync_settings: SyncSettings = field(default_factory=SyncSettings)
    log_level: str = "INFO"
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'GlobalConfiguration':
        """Create from dictionary."""
        # Convert nested objects
        if 'cloud_storage' in data and isinstance(data['cloud_storage'], dict):
            data['cloud_storage'] = CloudStorageConfig.from_dict(data['cloud_storage'])
        
        if 'browser_settings' in data and isinstance(data['browser_settings'], dict):
            data['browser_settings'] = BrowserSettings.from_dict(data['browser_settings'])
        
        if 'sync_settings' in data and isinstance(data['sync_settings'], dict):
            data['sync_settings'] = SyncSettings.from_dict(data['sync_settings'])
        
        return cls(**data)
    
    def save_to_file(self, file_path: str):
        """Save configuration to JSON file."""
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
    
    @classmethod
    def load_from_file(cls, file_path: str) -> 'GlobalConfiguration':
        """Load configuration from JSON file."""
        if not os.path.exists(file_path):
            # Return default configuration if file doesn't exist
            return cls()
        
        with open(file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        return cls.from_dict(data)


# Utility functions for creating default configurations

def create_default_website_profile(website_id: str, display_name: str, target_url: str) -> WebsiteProfile:
    """Create a default website profile with sensible defaults."""
    return WebsiteProfile(
        website_id=website_id,
        display_name=display_name,
        target_url=target_url,
        auth_method=AuthMethod.MANUAL
    )


def create_user_session(website_id: str, username: str, auth_state: Dict[str, Any]) -> UserSession:
    """Create a new user session."""
    session_id = f"{username}_{website_id}_{int(time.time())}"
    return UserSession(
        session_id=session_id,
        website_id=website_id,
        username=username,
        auth_state=auth_state,
        status=SessionStatus.ACTIVE
    )


def get_default_global_config() -> GlobalConfiguration:
    """Get default global configuration with current project settings."""
    # Import current config values
    try:
        from config import USERNAME, TARGET_URL
        cloud_credentials = {
            "access_key_id": "LTAI5t6eT7CRkj6hTQrTVXxE",
            "access_key_secret": "JL9FWMfdg42QR2HKBIhPLIE43yBmr3"
        }
    except ImportError:
        cloud_credentials = {}
    
    return GlobalConfiguration(
        cloud_storage=CloudStorageConfig(
            provider="aliyun_oss",
            credentials=cloud_credentials,
            bucket_name="auth-share",
            region="cn-shenzhen"
        ),
        browser_settings=BrowserSettings(
            default_count=1,
            max_concurrent=10,
            default_timeout=30,
            extensions_backup=True
        ),
        sync_settings=SyncSettings(
            auto_sync=True,
            sync_interval_minutes=30,
            conflict_resolution="timestamp"
        ),
        log_level="INFO"
    )